/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.tedu.mall.sso.security.service.user.impl;

import cn.tedu.mall.common.config.CoolSharkStaticPrefixConfiguration;
import cn.tedu.mall.common.exception.CoolSharkServiceException;
import cn.tedu.mall.common.pojo.sso.authentication.CsmallAuthenticationInfo;
import cn.tedu.mall.common.pojo.ums.model.User;
import cn.tedu.mall.common.pojo.ums.model.UserLoginLog;
import cn.tedu.mall.common.restful.ResponseCode;
import cn.tedu.mall.common.utils.JwtTokenUtils;
import cn.tedu.mall.sso.mapper.user.UserLoginLogMapper;
import cn.tedu.mall.sso.mapper.user.UserMapper;
import cn.tedu.mall.sso.pojo.domain.UserUserDetails;
import cn.tedu.mall.sso.pojo.dto.UserLoginDTO;
import cn.tedu.mall.sso.pojo.dto.UserPhoneLoginDTO;
import cn.tedu.mall.sso.security.service.user.IUserSSOService;
import cn.tedu.mall.sso.security.service.user.UserPhoneSSOUserDetailsService;
import cn.tedu.mall.sso.security.service.user.UserSSOUserDetailsService;
import com.alibaba.druid.util.StringUtils;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class UserSSOServiceImpl implements IUserSSOService {

    @Autowired
    private UserSSOUserDetailsService userDetailsService;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private JwtTokenUtils jwtTokenUtils;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private UserLoginLogMapper userLoginLogMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private UserPhoneSSOUserDetailsService userPhoneSSOUserDetailsService;

    @Override
    public String doLogin(UserLoginDTO userLoginDTO) {
        String token;
        UserUserDetails userDetails = (UserUserDetails) userDetailsService.loadUserByUsername(userLoginDTO.getUsername());
        if (userDetails == null) {
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST, "登录失败！用户名密码错误");
        }
        if (!passwordEncoder.matches(userLoginDTO.getPassword(), userDetails.getPassword())) {
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST, "登录失败！用户名密码错误");
        }
        CsmallAuthenticationInfo userInfo = generateFromUser(userDetails);
        token = jwtTokenUtils.generateToken(userInfo);
        //记录登录时间
        UserLoginLog userLoginLog = new UserLoginLog();
        userLoginLog.setUserId(userDetails.getId());
        LocalDateTime now = LocalDateTime.now();
        userLoginLog.setGmtCreate(now);
        userLoginLog.setGmtModified(now);
        userLoginLog.setGmtLogin(now);
        userLoginLog.setIp(userLoginDTO.getIp());
        userLoginLog.setUserAgent(userLoginDTO.getUserAgent());
        userLoginLog.setUsername(userDetails.getUsername());
        userLoginLog.setNickname(userDetails.getNickname());
        userLoginLogMapper.insertUserLoginLog(userLoginLog);
        return token;
    }

    public CsmallAuthenticationInfo generateFromUser(UserUserDetails userDetails) {
        List<GrantedAuthority> authorities = (List<GrantedAuthority>) userDetails.getAuthorities();
        List<String> authorityValues = new ArrayList<>();
        for (GrantedAuthority authority : authorities) {
            String value = authority.getAuthority();
            authorityValues.add(value);
        }
        CsmallAuthenticationInfo userInfo = new CsmallAuthenticationInfo();
        userInfo.setId(userDetails.getId());
        userInfo.setUsername(userDetails.getUsername());
        userInfo.setUserType("USER");
        userInfo.setAuthorities(authorityValues);
        return userInfo;

    }

    @Value("${jwt.tokenHead}")
    private String tokenHead;//Bearer

    @Override
    public void doLogout(String token) {
        if (token != null && token.startsWith(tokenHead)) {
            //拿到jwt token
            String authToken = token.substring(tokenHead.length());
            //写入redis 锁住 这里采用list分日期存储,方便后续定时清理
            String lockedTokenList = "token_list_.lock";
            stringRedisTemplate.boundSetOps(lockedTokenList).add(authToken);
            Long add = stringRedisTemplate.boundSetOps(lockedTokenList).add(authToken);
            if (add == 0) {
                throw new CoolSharkServiceException(ResponseCode.CONFLICT, "当前用户已经登出,不必重复登出");
            }
        } else {
            //header不存在token
            throw new CoolSharkServiceException(ResponseCode.NOT_FOUND, "当前客户端没有登录状态");
        }
    }

    @Override
    public void phoneExists(String phoneNum) {
        //验证正则
        validatePhone(phoneNum);
        //查询是否存在电话号
        int count = userMapper.selectUserCountByUserphone(phoneNum);
        if (count == 0) {
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST, "您的电话号码不存在");
        }
    }

    @Override
    public String phoneLogin(UserPhoneLoginDTO userPhoneLoginDTO) {
        validatePhone(userPhoneLoginDTO.getPhoneNum());
        //验证code是否正确
        verifyCode(userPhoneLoginDTO);
        //调用loadUserByUsername 如果查不到创建游客用户
        UserUserDetails userDetails = (UserUserDetails) userPhoneSSOUserDetailsService.loadUserByUsername(userPhoneLoginDTO.getPhoneNum());
        if (userDetails == null) {
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST, "登录失败！未知原因");
        }
        LocalDateTime now = LocalDateTime.now();
        User user = userMapper.findByPhone(userPhoneLoginDTO.getPhoneNum());
        if (user == null) {
            //在数据库注册
            user = new User();
            BeanUtils.copyProperties(userDetails, user);
            user.setLastLoginIp(userPhoneLoginDTO.getIp());
            user.setGmtModified(now);
            user.setGmtCreate(now);
            user.setEnable(userDetails.getIsEnable());
            user.setEmail(UUID.randomUUID().toString());
            userMapper.save(user);
        }

        CsmallAuthenticationInfo userInfo = generateFromUser(userDetails);
        if (userInfo.getId() == null) {
            userInfo.setId(user.getId());
        }
        String token = jwtTokenUtils.generateToken(userInfo);
        //记录登录时间
        UserLoginLog userLoginLog = new UserLoginLog();
        userLoginLog.setUserId(userDetails.getId());

        userLoginLog.setGmtCreate(now);
        userLoginLog.setGmtModified(now);
        userLoginLog.setGmtLogin(now);
        userLoginLog.setIp(userPhoneLoginDTO.getIp());
        userLoginLog.setUserAgent(userPhoneLoginDTO.getUserAgent());
        userLoginLog.setUsername(userDetails.getUsername());
        userLoginLog.setNickname(userDetails.getNickname());
        userLoginLogMapper.insertUserLoginLog(userLoginLog);
        return token;

    }

    private void verifyCode(UserPhoneLoginDTO userPhoneLoginDTO) {
        if (StringUtils.isEmpty(userPhoneLoginDTO.getCode()) || StringUtils.isEmpty(userPhoneLoginDTO.getPhoneNum())) {
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST, "请填写手机号和验证码");
        }
        String codeKey = CoolSharkStaticPrefixConfiguration.SSOPrefixConfiguration.CODE_SMS_PREFIX + userPhoneLoginDTO.getPhoneNum();
        Boolean hasCodeKey = stringRedisTemplate.hasKey(codeKey);
        ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
        if (!hasCodeKey) {
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST, "您的手机号没有发送验证码");
        } else {
            String redisCode = opsForValue.get(codeKey);
            if (StringUtils.isEmpty(redisCode)) {
                throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST, "您的手机号没有生成验证码");
            }
            if (!StringUtils.equals(redisCode, userPhoneLoginDTO.getCode())) {
                throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST, "您填写的验证不正确");
            }
            stringRedisTemplate.delete(codeKey);
        }

    }

    private void validatePhone(String phoneNum) {
        String regex = "^1[34589][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$";
        if (!(phoneNum.matches(regex))) {
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST, "你输入的电话号码不正确");
        }
    }

}
